Skip to content

fix: tolerate missing created_at/updated_at in LinkedAccount responses#148

Merged
mstrivens merged 1 commit into
mainfrom
fix/linkedaccount-optional-timestamps
Jul 21, 2026
Merged

fix: tolerate missing created_at/updated_at in LinkedAccount responses#148
mstrivens merged 1 commit into
mainfrom
fix/linkedaccount-optional-timestamps

Conversation

@mstrivens

Copy link
Copy Markdown
Contributor

Problem

accounts.list_linked_accounts (GET /accounts) fails to deserialize on every call for some providers. Reported against 0.40.1; still broken on 0.41.0 (fails differently — see below).

Root cause is a spec-vs-reality mismatch on the nested LinkedAccount:

  • The spec marks created_at and updated_at as required + non-nullable.
  • The live API omits them for some providers (the bug report's linkedin_lms example has neither).

Crystalline's strict from_dict raises KeyError: key created_at not found in hash while building each nested LinkedAccount. The /accounts 200 response is a oneOf whose branches both wrap LinkedAccount, so both fail and the whole page is lost over two absent timestamps on one record:

0.40.1 0.41.0
Unmarshal target bare Array<LinkedAccount> Union<LinkedAccountsPaginated, Array<LinkedAccount>>
Failure TypeError raised returns nil silently

Evidence the report's example is a real capture, not trimmed: it includes a metadata field that isn't in the OpenAPI schema at all.

Fix

Overlay actions that make created_at/updated_at nullable and drop them from LinkedAccount.required, so the generated model deserializes accounts that lack them. (Both changes are needed: nullable alone leaves them keyword-required in the constructor → ArgumentError.)

Verification

Regenerated locally (speakeasy 1.638.0, lint 0 errors, SDK generated successfully) and deserialized:

  • the exact payload from the bug report (timestamps absent) → LinkedAccountsPaginated, data.size=1, timestamps nil
  • a full payload with timestamps → parses them correctly ✓
  • the bare-array oneOf branch → Array<LinkedAccount>

Follow-up (not in this PR)

Stopgap in the Ruby overlay. The real fix belongs in the OAS producer (unified-cloud-api): either always return created_at/updated_at, or mark them nullable there — that covers the TS and PHP SDKs too, which have the same latent break.

🤖 Generated with Claude Code

list_linked_accounts (GET /accounts) fails to deserialize on every call
for some providers. The live response omits created_at/updated_at (e.g.
linkedin_lms accounts), but the spec marks both required + non-nullable.

Crystalline's strict from_dict raises KeyError on the missing key while
building each nested LinkedAccount. Because the /accounts 200 response is
a oneOf whose branches both wrap LinkedAccount, both branches fail and the
Union returns nil (0.41.0) / raised TypeError (0.40.1) — the entire page
is lost over two absent timestamps on one record.

Make created_at/updated_at nullable and drop them from LinkedAccount's
required list so the generated model deserializes accounts that lack them.

Verified by regenerating locally (speakeasy 1.638.0, lint 0 errors, SDK
generated successfully) and deserializing:
  - the exact payload from the bug report (timestamps absent) -> ok
  - a full payload with timestamps present -> ok, timestamps parsed
  - the bare-array oneOf branch -> ok

Stopgap in the Ruby overlay; the real fix belongs in the OAS producer
(make the fields nullable there, or always return them) to cover all SDKs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mstrivens
mstrivens requested a review from a team as a code owner July 21, 2026 09:24
Copilot AI review requested due to automatic review settings July 21, 2026 09:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Speakeasy Ruby overlay to align the generated LinkedAccount model with observed /accounts API responses that sometimes omit created_at and updated_at, preventing deserialization failures when those timestamps are missing.

Changes:

  • Marks LinkedAccount.created_at and LinkedAccount.updated_at as nullable.
  • Removes created_at/updated_at from LinkedAccount.required and redefines the remaining required fields to keep the model constructible when timestamps are absent.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@CameronCarlin CameronCarlin left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@mstrivens
mstrivens merged commit 7e6eb82 into main Jul 21, 2026
1 check passed
@mstrivens
mstrivens deleted the fix/linkedaccount-optional-timestamps branch July 21, 2026 09:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants